package com.xyy.saas.payment.adpater.wxPayHandler;

import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.dao.model.EntrustPayInfo;
import com.xyy.saas.payment.dao.model.WxSettings;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.HttpClientUtil;
import com.xyy.saas.payment.util.SpringUtils;
import com.xyy.saas.payment.util.WxUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class WxEntrustAdapterService {

    private static final Logger LOGGER = LoggerFactory.getLogger(WxEntrustAdapterService.class);

    private RedisClient redisClient = SpringUtils.getBean("redisClient", RedisClient.class);

    private ConcurrentHashMap<String, Method> concurrentHashMap = new ConcurrentHashMap();

    private static WxEntrustAdapterService instance;

    private Class WxEntrustAdapterService = Class.forName("com.xyy.saas.payment.adpater.wxPayHandler.WxEntrustAdapterService");

    public WxEntrustAdapterService() throws ClassNotFoundException {
    }

    public static WxEntrustAdapterService getInstance(){
        if (null == instance){
            synchronized (WxEntrustAdapterService.class){
                if (null == instance){
                    try {
                        instance = new WxEntrustAdapterService();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return instance;
    }

    public String invokerEntrustUrl(String methodName, Map<String, Object> params) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        methodName = methodName+"EntrustUrl";
        Method method = concurrentHashMap.get(methodName);
        if (method==null){
            method = WxEntrustAdapterService.getMethod(methodName, Map.class);
            concurrentHashMap.put(methodName, method);
        }
        return String.valueOf(method.invoke(getInstance(), params));
    }

    public String h5EntrustUrl(Map<String, Object> params){
        EntrustPayInfo entrustPayInfo = (EntrustPayInfo) params.get("entrustPayInfo");
        WxSettings wxSettings = (WxSettings) params.get("wxSettings");
        String clientIp = (String) params.get("clientIp");

        Map<String, Object> signParam = preLoadCommonParam(params);
        signParam.put("clientip",clientIp);
        String sign = WxUtil.sha256_HMAC(signParam, wxSettings.getApiSecret());

        StringBuilder entrustUrl = new StringBuilder();
        entrustUrl.append(ApolloUtil.getProperty("wx.entrust.H5Url")+"?");
        entrustUrl.append("clientip="+signParam.get("clientip") + "&");
        joinCommonParam(entrustUrl, signParam);
        entrustUrl.append("sign=" + sign);

        try {
            String redirectUrl = redisClient.get(Constants.ENTRUST_H5_KEY + entrustPayInfo.getSignMode() + entrustPayInfo.getContractCode());
            if(!StringUtils.isBlank(redirectUrl)) {
                return redirectUrl;
            }
            String s = HttpClientUtil.xyyHttpGetRequest(entrustUrl.toString());
            SortedMap<String, String> resultMap = WxUtil.doXMLParse(s);
            if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
                redirectUrl = resultMap.get("redirect_url");
                LOGGER.info("WxCommonAdapter#entrustSign H5 get redirect_url success. redirectUrl:{},entrustUrl:{}", redirectUrl, entrustUrl);
                // 保存到redis 有效期10分钟
                redisClient.setEx(Constants.ENTRUST_H5_KEY+entrustPayInfo.getSignMode()+entrustPayInfo.getContractCode(),redirectUrl, 600);
                return redirectUrl;
            }
            LOGGER.error("WxCommonAdapter#entrustSign H5 get redirect_url fail. entrustUrl:{}, return_msg:{}, result_msg:{}", entrustUrl, resultMap.get("return_msg"), resultMap.get("result_msg"));
            throw new PaymentException(resultMap.get("return_msg")+" - "+resultMap.get("result_msg"));
        } catch (Exception e) {
            LOGGER.info("WxCommonAdapter#entrustSign H5 get redirect_url request fail. error:{}",e);
            return "";
        }
    }

    private Map<String, Object> preLoadCommonParam(Map<String, Object> params) {
        EntrustPayInfo entrustPayInfo = (EntrustPayInfo) params.get("entrustPayInfo");
        String requestSerial = (String) params.get("requestSerial");
        WxSettings wxSettings = (WxSettings) params.get("wxSettings");

        //签名生成
        Map<String, Object> signParam = new TreeMap<>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });

        // 公共参数
        signParam.put("appid", wxSettings.getWxAppId());
        signParam.put("mch_id", wxSettings.getMerchantId());
        signParam.put("contract_code", entrustPayInfo.getContractCode());
        signParam.put("contract_display_account", entrustPayInfo.getContractDisplayAccount());
        signParam.put("notify_url", ApolloUtil.getProperty("wx.notifyUrl.entrustSign"));
        signParam.put("version", "1.0");
        signParam.put("plan_id", entrustPayInfo.getPlanId());
        signParam.put("outerid", entrustPayInfo.getContractDisplayAccount());
        signParam.put("request_serial", requestSerial);
        signParam.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));

        return signParam;
    }

    public String pubEntrustUrl(Map<String, Object> params){
        WxSettings wxSettings = (WxSettings) params.get("wxSettings");

        Map<String, Object> signParam = preLoadCommonParam(params);
        signParam.put("return_web", "1");
        String sign = WxUtil.getSign(signParam, wxSettings.getApiSecret());

        StringBuilder entrustUrl = new StringBuilder();
        entrustUrl.append(ApolloUtil.getProperty("wx.entrust.webUrl")+"?");
        entrustUrl.append("return_web=" + 1 + "&");
        entrustUrl.append("outerid=" + signParam.get("contract_display_account") + "&");
        joinCommonParam(entrustUrl, signParam);
        entrustUrl.append("sign=" + sign);
        return entrustUrl.toString();
    }

    private void joinCommonParam(StringBuilder entrustUrl, Map<String, Object> signParam){
        entrustUrl.append("appid=" + signParam.get("appid") + "&");
        entrustUrl.append("contract_code=" + signParam.get("contract_code") + "&");
        entrustUrl.append("contract_display_account=" + signParam.get("contract_display_account") + "&");
        entrustUrl.append("mch_id=" + signParam.get("mch_id") + "&");
        entrustUrl.append("notify_url=" + URLEncoder.encode(String.valueOf(signParam.get("notify_url"))) + "&");
        entrustUrl.append("version=" + signParam.get("version") + "&");
        entrustUrl.append("plan_id=" + signParam.get("plan_id") + "&");
        entrustUrl.append("outerid=" + signParam.get("outerid") + "&");
        entrustUrl.append("request_serial=" + signParam.get("request_serial") + "&");
        entrustUrl.append("timestamp=" + signParam.get("timestamp") + "&");
    }
}
